﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using LogicalComponent.util;
using LogicalComponent.util.exception;

namespace TestLogicalComponent.util
{
    [TestClass]
    public class TestFraction
    {
        [TestMethod]
        public void TestConstructor()
        {
            Fraction f1 = new Fraction(4);
            Assert.AreEqual(f1.Numerator, 4);
            Assert.AreEqual(f1.Denominator, 1);

            Fraction f2 = new Fraction(5, 3);
            Assert.AreEqual(f2.Numerator, 5);
            Assert.AreEqual(f2.Denominator, 3);

            Fraction f3 = new Fraction(-5, 3);
            Assert.AreEqual(f3.Numerator, -5);
            Assert.AreEqual(f3.Denominator, 3);

            Fraction f4 = new Fraction(-5);
            Assert.AreEqual(f4.Numerator, -5);
            Assert.AreEqual(f4.Denominator, 1);

            Fraction f5 = new Fraction(new Fraction(2, 3));
            Assert.AreEqual(f5.Numerator, 2);
            Assert.AreEqual(f5.Denominator, 3);

            try
            {
                Fraction f = new Fraction(4, 0);
                Assert.Fail();
            }
            catch (LogicalUserError) { }
        }

        [TestMethod]
        public void TestReduce()
        {
            Fraction f1 = new Fraction(24, 6);
            Assert.AreEqual(f1.Numerator, 4);
            Assert.AreEqual(f1.Denominator, 1);

            Fraction f2 = new Fraction(10, 15);
            Assert.AreEqual(f2.Numerator, 2);
            Assert.AreEqual(f2.Denominator, 3);

            Fraction f3 = new Fraction(-4, 6);
            Assert.AreEqual(f3.Numerator, -2);
            Assert.AreEqual(f3.Denominator, 3);

            Fraction f4 = new Fraction(4, -6);
            Assert.AreEqual(f4.Numerator, -2);
            Assert.AreEqual(f4.Denominator, 3);

            Fraction f5 = new Fraction(-2, -3);
            Assert.AreEqual(f5.Numerator, 2);
            Assert.AreEqual(f5.Denominator, 3);
        }

        [TestMethod]
        public void TestEquals() 
        {
            Tuple<Fraction, Fraction>[] ffEqualsSets = { 
                              new Tuple<Fraction, Fraction>(new Fraction(10), new Fraction(20, 2)),
                              new Tuple<Fraction, Fraction>(new Fraction(1), new Fraction(1)),
                              new Tuple<Fraction, Fraction>(new Fraction(0), new Fraction(0)),
                              new Tuple<Fraction, Fraction>(new Fraction(0, 3), new Fraction(0, 16)),
                              new Tuple<Fraction, Fraction>(new Fraction(4, 6), new Fraction(6, 9)),
                              new Tuple<Fraction, Fraction>(new Fraction(20000, 1500), new Fraction(40000, 3000)),
                                                     };
            foreach(Tuple<Fraction, Fraction> set in ffEqualsSets)
            {
                Assert.AreEqual(set.Item1, set.Item2);
                Assert.AreEqual<Fraction>(set.Item1, set.Item2);
                Assert.IsTrue(set.Item1 == set.Item2);
                Assert.IsTrue(set.Item2 == set.Item1);
                Assert.IsFalse(set.Item1 != set.Item2);
                Assert.IsFalse(set.Item2 != set.Item1);
            }

            Tuple<Fraction, int>[] fiEqualsSets = { 
                              new Tuple<Fraction, int>(new Fraction(20, 2), 10),
                              new Tuple<Fraction, int>(new Fraction(1), 1),
                              new Tuple<Fraction, int>(new Fraction(0), 0),
                                                  };
            foreach (Tuple<Fraction, int> set in fiEqualsSets)
            {
                Assert.AreEqual(set.Item1, set.Item2);
                Assert.IsTrue(set.Item1.Equals(set.Item2));
                Assert.IsTrue(set.Item1 == set.Item2);
                Assert.IsTrue(set.Item2 == set.Item1);
                Assert.IsFalse(set.Item1 != set.Item2);
                Assert.IsFalse(set.Item2 != set.Item1);
            }
        }
        [TestMethod]
        public void TestNotEquals()
        {
            Tuple<Fraction, Fraction>[] notEqualsSets = { 
                              new Tuple<Fraction, Fraction>(new Fraction(10), new Fraction(30, 2)),
                              new Tuple<Fraction, Fraction>(new Fraction(1), new Fraction(0)),
                              new Tuple<Fraction, Fraction>(new Fraction(0), new Fraction(1)),
                              new Tuple<Fraction, Fraction>(new Fraction(4, 6), new Fraction(6, 10)),
                              new Tuple<Fraction, Fraction>(new Fraction(20000, 1500), new Fraction(40000, 3500)),
                                                        };
            foreach (Tuple<Fraction, Fraction> set in notEqualsSets)
            {
                Assert.AreNotEqual(set.Item1, set.Item2);
                Assert.IsFalse(set.Item1 == set.Item2);
                Assert.IsTrue(set.Item1 != set.Item2);
            }

            Tuple<Fraction, int>[] fiEqualsSets = { 
                              new Tuple<Fraction, int>(new Fraction(10, 3), 10),
                              new Tuple<Fraction, int>(new Fraction(2), 1),
                              new Tuple<Fraction, int>(new Fraction(3), 0),
                                                  };
            foreach (Tuple<Fraction, int> set in fiEqualsSets)
            {
                Assert.AreNotEqual(set.Item1, set.Item2);
                Assert.IsFalse(set.Item1.Equals(set.Item2));
                Assert.IsFalse(set.Item1 == set.Item2);
                Assert.IsFalse(set.Item2 == set.Item1);
                Assert.IsTrue(set.Item1 != set.Item2);
                Assert.IsTrue(set.Item2 != set.Item1);
            }

            Assert.IsFalse(new Fraction(1).Equals(new object()));
        }

        [TestMethod]
        public void TestNullEquivalence()
        {
            Fraction isNull = null;
            Fraction isNull2 = null;
            Fraction actual = new Fraction(1, 2);

            Assert.AreNotEqual(actual, isNull);
            Assert.AreNotEqual(isNull, actual);
            Assert.AreNotEqual<Fraction>(actual, isNull);
            Assert.AreNotEqual<Fraction>(isNull, actual);
            Assert.IsFalse(actual == isNull);
            Assert.IsFalse(isNull == actual);
            Assert.IsTrue(actual != isNull);
            Assert.IsTrue(isNull != actual);

            Assert.AreEqual(isNull, isNull2);
            Assert.AreEqual<Fraction>(isNull, isNull2);
            Assert.IsTrue(isNull == isNull2);
            Assert.IsFalse(isNull != isNull2);
        }

        [TestMethod]
        public void TestSubtraction()
        {
            Fraction f1 = new Fraction(8, 3);
            Fraction f2 = new Fraction(8, 3);
            Fraction f3 = new Fraction(2, 3);
            Assert.IsTrue(f1-- == f2);
            Assert.IsTrue(--f1 == f3);
            Assert.IsTrue(f1 == f3);

            Tuple<Fraction, Fraction, Fraction>[] equationsff = {
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(5, 6), new Fraction(2, 6), new Fraction(3, 6)),
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(2, 5), new Fraction(2, 6), new Fraction(1, 15)),
                                                              };
            foreach (Tuple<Fraction, Fraction, Fraction> equation in equationsff)
            {
                Assert.IsTrue(equation.Item1 - equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f -= equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<Fraction, int, Fraction>[] equationsfi = {
                         new Tuple<Fraction, int, Fraction>(new Fraction(5, 6), 1, new Fraction(-1, 6)),
                         new Tuple<Fraction, int, Fraction>(new Fraction(7, 3), 2, new Fraction(1, 3)),
                                                           };
            foreach (Tuple<Fraction, int, Fraction> equation in equationsfi)
            {
                Assert.IsTrue(equation.Item1 - equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f -= equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<int, Fraction, Fraction>[] equationsif = {
                         new Tuple<int, Fraction, Fraction>(3, new Fraction(-5, 6), new Fraction(23, 6)),
                         new Tuple<int, Fraction, Fraction>(-3, new Fraction(7, 3), new Fraction(-16, 3)),
                         new Tuple<int, Fraction, Fraction>(2, new Fraction(6, 3), new Fraction(0, 1)),
                                                           };
            foreach (Tuple<int, Fraction, Fraction> equation in equationsif)
            {
                Assert.IsTrue(equation.Item1 - equation.Item2 == equation.Item3);
            }
        }

        [TestMethod]
        public void TestAddition()
        {
            Fraction f1 = new Fraction(2, 3);
            Fraction f2 = new Fraction(2, 3);
            Fraction f3 = new Fraction(8, 3);
            Assert.IsTrue(f1++ == f2);
            Assert.IsTrue(++f1 == f3);
            Assert.IsTrue(f1 == f3);

            Tuple<Fraction, Fraction, Fraction>[] equationsff = {
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(5, 6), new Fraction(2, 6), new Fraction(7, 6)),
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(3, 5), new Fraction(2, 6), new Fraction(14, 15)),
                                                              };
            foreach (Tuple<Fraction, Fraction, Fraction> equation in equationsff)
            {
                Assert.IsTrue(equation.Item1 + equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f += equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<Fraction, int, Fraction>[] equationsfi = {
                         new Tuple<Fraction, int, Fraction>(new Fraction(5, 6), -3, new Fraction(-13, 6)),
                         new Tuple<Fraction, int, Fraction>(new Fraction(3, 5), 5, new Fraction(28, 5)),
                                                              };
            foreach (Tuple<Fraction, int, Fraction> equation in equationsfi)
            {
                Assert.IsTrue(equation.Item1 + equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f += equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<int, Fraction, Fraction>[] equationsif = {
                         new Tuple<int, Fraction, Fraction>(3, new Fraction(-5, 6), new Fraction(13, 6)),
                         new Tuple<int, Fraction, Fraction>(-3, new Fraction(7, 3), new Fraction(-2, 3)),
                         new Tuple<int, Fraction, Fraction>(2, new Fraction(-6, 3), new Fraction(0, 1)),
                                                           };
            foreach (Tuple<int, Fraction, Fraction> equation in equationsif)
            {
                Assert.IsTrue(equation.Item1 + equation.Item2 == equation.Item3);
            }
        }

        [TestMethod]
        public void TestMultiplication()
        {
            Tuple<Fraction, Fraction, Fraction>[] equationsff = {
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(5, 6), new Fraction(2, 6), new Fraction(5, 18)),
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(3, 5), new Fraction(2, 6), new Fraction(1, 5)),
                                                              };
            foreach (Tuple<Fraction, Fraction, Fraction> equation in equationsff)
            {
                Assert.IsTrue(equation.Item1 * equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f *= equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<Fraction, int, Fraction>[] equationsfi = {
                         new Tuple<Fraction, int, Fraction>(new Fraction(5, 6), -3, new Fraction(-15, 6)),
                         new Tuple<Fraction, int, Fraction>(new Fraction(3, 5), 5, new Fraction(15, 5)),
                                                              };
            foreach (Tuple<Fraction, int, Fraction> equation in equationsfi)
            {
                Assert.IsTrue(equation.Item1 * equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f *= equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<int, Fraction, Fraction>[] equationsif = {
                         new Tuple<int, Fraction, Fraction>(3, new Fraction(-5, 6), new Fraction(-15, 6)),
                         new Tuple<int, Fraction, Fraction>(-3, new Fraction(-7, 3), new Fraction(21, 3)),
                         new Tuple<int, Fraction, Fraction>(2, new Fraction(0, 3), new Fraction(0, 1)),
                                                           };
            foreach (Tuple<int, Fraction, Fraction> equation in equationsif)
            {
                Assert.IsTrue(equation.Item1 * equation.Item2 == equation.Item3);
            }
        }

        [TestMethod]
        public void TestDivision()
        {
            Tuple<Fraction, Fraction, Fraction>[] equationsff = {
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(0, 6), new Fraction(16, 6), new Fraction(0, 1)),
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(3, 5), new Fraction(2, 6), new Fraction(9, 5)),
                         new Tuple<Fraction, Fraction, Fraction>(new Fraction(-3, 5), new Fraction(-2, 7), new Fraction(21, 10)),
                                                              };
            foreach (Tuple<Fraction, Fraction, Fraction> equation in equationsff)
            {
                Assert.IsTrue(equation.Item1 / equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f /= equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<Fraction, int, Fraction>[] equationsfi = {
                         new Tuple<Fraction, int, Fraction>(new Fraction(5, 6), -3, new Fraction(-5, 18)),
                         new Tuple<Fraction, int, Fraction>(new Fraction(-3, 5), 5, new Fraction(-3, 25)),
                                                              };
            foreach (Tuple<Fraction, int, Fraction> equation in equationsfi)
            {
                Assert.IsTrue(equation.Item1 / equation.Item2 == equation.Item3);
                Fraction f = new Fraction(equation.Item1);
                f /= equation.Item2;
                Assert.IsTrue(f == equation.Item3);
            }

            Tuple<int, Fraction, Fraction>[] equationsif = {
                         new Tuple<int, Fraction, Fraction>(3, new Fraction(-5, 6), new Fraction(-18, 5)),
                         new Tuple<int, Fraction, Fraction>(-3, new Fraction(-7, 3), new Fraction(9, 7)),
                         new Tuple<int, Fraction, Fraction>(2, new Fraction(-6, 3), new Fraction(-1, 1)),
                                                           };
            foreach (Tuple<int, Fraction, Fraction> equation in equationsif)
            {
                Assert.IsTrue(equation.Item1 / equation.Item2 == equation.Item3);
            }
        }

        [TestMethod]
        public void TestComparisons()
        {
            Fraction[] fractions = {new Fraction(-16, 5), new Fraction(-40, 21), new Fraction(0, 1), new Fraction(11, 7), new Fraction(5, 2)};
            int[] ints = { -5, -2, -1, 1, 2, 3 };

            for (int i = 0; i < fractions.Count(); i++) 
            {
                Fraction current = fractions[i];

                for (int j = 0; j < fractions.Count(); j++) 
                {
                    Fraction other = fractions[j];
                    if (j < i) {
                        Assert.IsTrue(other < current);
                        Assert.IsFalse(current < other);
                        Assert.IsTrue(other <= current);
                        Assert.IsFalse(current <= other);
                        Assert.IsFalse(other > current);
                        Assert.IsTrue(current > other);
                        Assert.IsFalse(other >= current);
                        Assert.IsTrue(current >= other);
                    }
                    else if (j == i)
                    {
                        Assert.IsFalse(other < current);
                        Assert.IsFalse(current < other);
                        Assert.IsTrue(other <= current);
                        Assert.IsTrue(current <= other);
                        Assert.IsFalse(other > current);
                        Assert.IsFalse(current > other);
                        Assert.IsTrue(other >= current);
                        Assert.IsTrue(current >= other);
                    }
                    else
                    {
                        Assert.IsFalse(other < current);
                        Assert.IsTrue(current < other);
                        Assert.IsFalse(other <= current);
                        Assert.IsTrue(current <= other);
                        Assert.IsTrue(other > current);
                        Assert.IsFalse(current > other);
                        Assert.IsTrue(other >= current);
                        Assert.IsFalse(current >= other);
                    }
                }

                for (int j = 0; j < ints.Count(); j++)
                {
                    int other = ints[j];
                    if (j <= i)
                    {
                        Assert.IsTrue(other < current);
                        Assert.IsFalse(current < other);
                        Assert.IsTrue(other <= current);
                        Assert.IsFalse(current <= other);
                        Assert.IsFalse(other > current);
                        Assert.IsTrue(current > other);
                        Assert.IsFalse(other >= current);
                        Assert.IsTrue(current >= other);
                    }
                    else
                    {
                        Assert.IsFalse(other < current);
                        Assert.IsTrue(current < other);
                        Assert.IsFalse(other <= current);
                        Assert.IsTrue(current <= other);
                        Assert.IsTrue(other > current);
                        Assert.IsFalse(current > other);
                        Assert.IsTrue(other >= current);
                        Assert.IsFalse(current >= other);
                    }
                }
            }
            int samplei = 5;
            Fraction samplef = new Fraction(10, 2);
            Assert.IsFalse(samplef < samplei);
            Assert.IsFalse(samplei < samplef);
            Assert.IsTrue(samplef <= samplei);
            Assert.IsTrue(samplei <= samplef);
            Assert.IsFalse(samplef > samplei);
            Assert.IsFalse(samplei > samplef);
            Assert.IsTrue(samplef >= samplei);
            Assert.IsTrue(samplei >= samplef);
        }
        
    }
}
